;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL

(ns app.main.ui.viewer.interactions
  (:require
   [app.common.data :as d]
   [app.common.geom.matrix :as gmt]
   [app.common.geom.point :as gpt]
   [app.common.pages :as cp]
   [app.common.types.page-options :as cto]
   [app.main.data.comments :as dcm]
   [app.main.data.viewer :as dv]
   [app.main.refs :as refs]
   [app.main.store :as st]
   [app.main.ui.components.dropdown :refer [dropdown]]
   [app.main.ui.icons :as i]
   [app.main.ui.viewer.shapes :as shapes]
   [app.util.dom :as dom]
   [app.util.i18n :as i18n :refer [tr]]
   [app.util.keyboard :as kbd]
   [goog.events :as events]
   [rumext.alpha :as mf]))

(defn prepare-objects
  [page frame]
  (fn []
    (let [objects   (:objects page)
          frame-id  (:id frame)
          modifier  (-> (gpt/point (:x frame) (:y frame))
                        (gpt/negate)
                        (gmt/translate-matrix))

          update-fn #(d/update-when %1 %2 assoc-in [:modifiers :displacement] modifier)]

      (->> (cp/get-children frame-id objects)
           (into [frame-id])
           (reduce update-fn objects)))))


(mf/defc viewport
  {::mf/wrap [mf/memo]}
  [{:keys [page interactions-mode frame base-frame frame-offset size]}]
  (let [objects       (mf/use-memo
                       (mf/deps page frame)
                       (prepare-objects page frame))

        wrapper       (mf/use-memo
                       (mf/deps objects)
                       #(shapes/frame-container-factory objects))

        ;; Retrieve frames again with correct modifier
        frame         (get objects (:id frame))
        base-frame    (get objects (:id base-frame))

        on-click
        (fn [_]
          (when (= interactions-mode :show-on-click)
            (st/emit! dv/flash-interactions)))

        on-mouse-wheel
        (fn [event]
          (when (or (kbd/ctrl? event) (kbd/meta? event))
            (dom/prevent-default event)
            (let [event (.getBrowserEvent ^js event)
                  delta (+ (.-deltaY ^js event) (.-deltaX ^js event))]
              (if (pos? delta)
                (st/emit! dv/decrease-zoom)
                (st/emit! dv/increase-zoom)))))

        on-key-down
        (fn [event]
          (when (kbd/esc? event)
            (st/emit! (dcm/close-thread))))]

    (mf/use-effect
      (mf/deps interactions-mode) ;; on-click event depends on interactions-mode
      (fn []
        ;; bind with passive=false to allow the event to be cancelled
        ;; https://stackoverflow.com/a/57582286/3219895
        (let [key1 (events/listen goog/global "wheel" on-mouse-wheel #js {"passive" false})
              key2 (events/listen js/window "keydown" on-key-down)
              key3 (events/listen js/window "click" on-click)]
          (fn []
            (events/unlistenByKey key1)
            (events/unlistenByKey key2)
            (events/unlistenByKey key3)))))

    [:& (mf/provider shapes/base-frame-ctx) {:value base-frame}
     [:& (mf/provider shapes/frame-offset-ctx) {:value frame-offset}
      [:svg {:view-box (:vbox size)
             :width (:width size)
             :height (:height size)
             :version "1.1"
             :xmlnsXlink "http://www.w3.org/1999/xlink"
             :xmlns "http://www.w3.org/2000/svg"}
       [:& wrapper {:shape frame
                    :view-box (:vbox size)}]]]]))


(mf/defc flows-menu
  {::mf/wrap [mf/memo]}
  [{:keys [page index]}]
  (let [flows        (get-in page [:options :flows])
        frames       (:frames page)
        frame        (get frames index)
        current-flow (mf/use-state
                       (cto/get-frame-flow flows (:id frame)))

        show-dropdown?  (mf/use-state false)
        toggle-dropdown (mf/use-fn #(swap! show-dropdown? not))
        hide-dropdown   (mf/use-fn #(reset! show-dropdown? false))

        select-flow
        (mf/use-callback
         (fn [flow]
           (reset! current-flow flow)
           (st/emit! (dv/go-to-frame (:starting-frame flow)))))]

    (when (seq flows)
      [:div.view-options {:on-click toggle-dropdown}
       [:span.icon i/play]
       [:span.label (:name @current-flow)]
       [:span.icon i/arrow-down]
       [:& dropdown {:show @show-dropdown?
                     :on-close hide-dropdown}
        [:ul.dropdown.with-check
         (for [flow flows]
           [:li {:class (dom/classnames :selected (= (:id flow) (:id @current-flow)))
                 :on-click #(select-flow flow)}
            [:span.icon i/tick]
            [:span.label (:name flow)]])]]])))


(mf/defc interactions-menu
  []
  (let [local           (mf/deref refs/viewer-local)
        mode            (:interactions-mode local)

        show-dropdown?  (mf/use-state false)
        toggle-dropdown (mf/use-fn #(swap! show-dropdown? not))
        hide-dropdown   (mf/use-fn #(reset! show-dropdown? false))

        select-mode
        (mf/use-callback
         (fn [mode]
           (st/emit! (dv/set-interactions-mode mode))))]

    [:div.view-options {:on-click toggle-dropdown}
     [:span.label (tr "viewer.header.interactions")]
     [:span.icon i/arrow-down]
     [:& dropdown {:show @show-dropdown?
                   :on-close hide-dropdown}
      [:ul.dropdown.with-check
       [:li {:class (dom/classnames :selected (= mode :hide))
             :on-click #(select-mode :hide)}
        [:span.icon i/tick]
        [:span.label (tr "viewer.header.dont-show-interactions")]]

       [:li {:class (dom/classnames :selected (= mode :show))
             :on-click #(select-mode :show)}
        [:span.icon i/tick]
        [:span.label (tr "viewer.header.show-interactions")]]

       [:li {:class (dom/classnames :selected (= mode :show-on-click))
             :on-click #(select-mode :show-on-click)}
        [:span.icon i/tick]
        [:span.label (tr "viewer.header.show-interactions-on-click")]]]]]))

